const Budget = require('../models/Budget');
const db = require('../config/db');

class BudgetController {
  // Create a new budget
  static async createBudget(req, res) {
    try {
      const { category_id, amount, period, start_date, end_date } = req.body;
      const user_id = req.userId; // Set by auth middleware
      
      // Validate required fields
      if (!category_id || !amount || !period || !start_date) {
        return res.status(400).json({ error: 'Category, amount, period, and start_date are required' });
      }
      
      // Validate amount
      const amountNum = parseFloat(amount);
      if (isNaN(amountNum) || amountNum <= 0) {
        return res.status(400).json({ error: 'Amount must be a positive number' });
      }
      
      // Validate period
      const validPeriods = ['daily', 'weekly', 'monthly', 'yearly'];
      if (!validPeriods.includes(period)) {
        return res.status(400).json({ error: 'Invalid period. Must be one of: daily, weekly, monthly, yearly' });
      }
      
      // Validate date format
      const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
      if (!dateRegex.test(start_date)) {
        return res.status(400).json({ error: 'Start date must be in YYYY-MM-DD format' });
      }
      
      if (end_date && !dateRegex.test(end_date)) {
        return res.status(400).json({ error: 'End date must be in YYYY-MM-DD format' });
      }
      
      // Check if category exists and belongs to user (or is a default category)
      const categoryResult = await db.query(
        `SELECT * FROM categories WHERE id = $1 AND (user_id = $2 OR user_id = 0)`,
        [category_id, user_id]
      );
      
      if (categoryResult.rows.length === 0) {
        return res.status(400).json({ error: 'Invalid category ID' });
      }
      
      // Create budget
      const result = await db.query(
        `INSERT INTO budgets (user_id, category_id, amount, period, start_date, end_date) 
         VALUES ($1, $2, $3, $4, $5, $6) RETURNING *`,
        [user_id, category_id, amountNum, period, start_date, end_date]
      );
      
      const budget = Budget.fromRow(result.rows[0]);
      
      res.status(201).json({
        message: 'Budget created successfully',
        budget: budget.toJSON()
      });
    } catch (error) {
      console.error('Create budget error:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
  
  // Get all budgets for a user
  static async getBudgets(req, res) {
    try {
      const user_id = req.userId; // Set by auth middleware
      
      // Get budgets
      const result = await db.query(
        `SELECT b.*, c.name as category_name FROM budgets b 
         LEFT JOIN categories c ON b.category_id = c.id 
         WHERE b.user_id = $1 ORDER BY b.start_date DESC`,
        [user_id]
      );
      
      const budgets = result.rows.map(row => {
        const budget = Budget.fromRow(row);
        return {
          ...budget.toJSON(),
          category_name: row.category_name
        };
      });
      
      res.json({
        budgets
      });
    } catch (error) {
      console.error('Get budgets error:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
  
  // Get a specific budget by ID
  static async getBudgetById(req, res) {
    try {
      const user_id = req.userId; // Set by auth middleware
      const budget_id = parseInt(req.params.id);
      
      // Validate budget_id
      if (isNaN(budget_id)) {
        return res.status(400).json({ error: 'Invalid budget ID' });
      }
      
      // Get budget
      const result = await db.query(
        `SELECT b.*, c.name as category_name FROM budgets b 
         LEFT JOIN categories c ON b.category_id = c.id 
         WHERE b.id = $1 AND b.user_id = $2`,
        [budget_id, user_id]
      );
      
      if (result.rows.length === 0) {
        return res.status(404).json({ error: 'Budget not found' });
      }
      
      const row = result.rows[0];
      const budget = Budget.fromRow(row);
      
      res.json({
        budget: {
          ...budget.toJSON(),
          category_name: row.category_name
        }
      });
    } catch (error) {
      console.error('Get budget error:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
  
  // Update a budget
  static async updateBudget(req, res) {
    try {
      const user_id = req.userId; // Set by auth middleware
      const budget_id = parseInt(req.params.id);
      
      // Validate budget_id
      if (isNaN(budget_id)) {
        return res.status(400).json({ error: 'Invalid budget ID' });
      }
      
      const { category_id, amount, period, start_date, end_date } = req.body;
      
      // Validate period if provided
      if (period) {
        const validPeriods = ['daily', 'weekly', 'monthly', 'yearly'];
        if (!validPeriods.includes(period)) {
          return res.status(400).json({ error: 'Invalid period. Must be one of: daily, weekly, monthly, yearly' });
        }
      }
      
      // Check if budget exists and belongs to user
      const existingBudget = await db.query(
        `SELECT * FROM budgets WHERE id = $1 AND user_id = $2`,
        [budget_id, user_id]
      );
      
      if (existingBudget.rows.length === 0) {
        return res.status(404).json({ error: 'Budget not found' });
      }
      
      // Process amount if provided
      let amountValue = existingBudget.rows[0].amount;
      if (amount !== undefined) {
        const amountNum = parseFloat(amount);
        if (isNaN(amountNum) || amountNum <= 0) {
          return res.status(400).json({ error: 'Amount must be a positive number' });
        }
        amountValue = amountNum;
      }
      
      // Update budget
      const result = await db.query(
        `UPDATE budgets SET 
         category_id = COALESCE($1, category_id), 
         amount = $2, 
         period = COALESCE($3, period), 
         start_date = COALESCE($4, start_date), 
         end_date = COALESCE($5, end_date),
         created_at = CURRENT_TIMESTAMP 
         WHERE id = $6 AND user_id = $7 RETURNING *`,
        [
          category_id, 
          amountValue, 
          period, 
          start_date, 
          end_date, 
          budget_id, 
          user_id
        ]
      );
      
      if (result.rows.length === 0) {
        return res.status(404).json({ error: 'Budget not found' });
      }
      
      const budget = Budget.fromRow(result.rows[0]);
      
      res.json({
        message: 'Budget updated successfully',
        budget: budget.toJSON()
      });
    } catch (error) {
      console.error('Update budget error:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
  
  // Delete a budget
  static async deleteBudget(req, res) {
    try {
      const user_id = req.userId; // Set by auth middleware
      const budget_id = parseInt(req.params.id);
      
      // Check if budget exists and belongs to user
      const existingBudget = await db.query(
        `SELECT * FROM budgets WHERE id = $1 AND user_id = $2`,
        [budget_id, user_id]
      );
      
      if (existingBudget.rows.length === 0) {
        return res.status(404).json({ error: 'Budget not found' });
      }
      
      // Delete budget
      await db.query(
        `DELETE FROM budgets WHERE id = $1 AND user_id = $2`,
        [budget_id, user_id]
      );
      
      res.json({
        message: 'Budget deleted successfully'
      });
    } catch (error) {
      console.error('Delete budget error:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
  
  // Get budget status (actual spending vs budget)
  static async getBudgetStatus(req, res) {
    try {
      const user_id = req.userId; // Set by auth middleware
      const date = req.query.date || new Date().toISOString().split('T')[0];
      
      // Validate date format
      if (date && !/^\d{4}-\d{2}-\d{2}$/.test(date)) {
        return res.status(400).json({ error: 'Invalid date format. Must be YYYY-MM-DD' });
      }
      
      // Get all budgets for the user
      const budgetsResult = await db.query(
        `SELECT b.*, c.name as category_name, c.type as category_type, c.color as category_color FROM budgets b 
         LEFT JOIN categories c ON b.category_id = c.id 
         WHERE b.user_id = $1 AND b.start_date <= $2 AND (b.end_date IS NULL OR b.end_date >= $2)`,
        [user_id, date]
      );
      
      // For each budget, calculate actual spending
      const budgetStatus = [];
      const rows = budgetsResult.rows || [];
      
      for (const row of rows) {
        const budget = Budget.fromRow(row);
        
        // Calculate actual spending for this budget's category and period
        let actualSpending = 0;
        
        // Determine the date range based on budget period
        let startDate, endDate;
        const budgetDate = new Date(budget.start_date);
        
        switch (budget.period) {
          case 'daily':
            startDate = budgetDate.toISOString().split('T')[0];
            endDate = startDate;
            break;
          case 'weekly':
            // Get start of week (Monday)
            const dayOfWeek = budgetDate.getDay();
            const diff = budgetDate.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1);
            startDate = new Date(budgetDate.setDate(diff)).toISOString().split('T')[0];
            endDate = new Date(new Date(startDate).setDate(new Date(startDate).getDate() + 6)).toISOString().split('T')[0];
            break;
          case 'monthly':
            startDate = new Date(budgetDate.getFullYear(), budgetDate.getMonth(), 1).toISOString().split('T')[0];
            endDate = new Date(budgetDate.getFullYear(), budgetDate.getMonth() + 1, 0).toISOString().split('T')[0];
            break;
          case 'yearly':
            startDate = new Date(budgetDate.getFullYear(), 0, 1).toISOString().split('T')[0];
            endDate = new Date(budgetDate.getFullYear(), 11, 31).toISOString().split('T')[0];
            break;
          default:
            startDate = budget.start_date;
            endDate = budget.end_date || new Date().toISOString().split('T')[0];
        }
        
        // Get actual spending for this category and date range
        const spendingResult = await db.query(
          `SELECT COALESCE(SUM(amount), 0) as total_spending FROM bills 
           WHERE user_id = $1 AND category_id = $2 AND type = 'expense' 
           AND bill_date::date >= $3 AND bill_date::date <= $4`,
          [user_id, budget.category_id, startDate, endDate]
        );
        
        if (spendingResult && spendingResult.rows && spendingResult.rows.length > 0) {
          actualSpending = parseFloat(spendingResult.rows[0].total_spending);
        }
        
        // Calculate percentage and determine if over budget
        const percentage = budget.amount > 0 ? (actualSpending / parseFloat(budget.amount)) * 100 : 0;
        const isOverBudget = actualSpending > parseFloat(budget.amount);
        
        budgetStatus.push({
          ...budget.toJSON(),
          category_name: row.category_name,
          category_type: row.category_type,
          category_color: row.category_color,
          actual_spending: actualSpending,
          percentage: percentage,
          is_over_budget: isOverBudget,
          start_date_range: startDate,
          end_date_range: endDate
        });
      }
      
      res.json({
        budget_status: budgetStatus
      });
    } catch (error) {
      console.error('Get budget status error:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
}

module.exports = BudgetController;